import commands, urllib, os.path, os, pickle, time, model, datetime, shutil
from tools import *
from settings import flickr
from utils import *

class MirrorController:
    """
    """
    
    def __init__(self, view):
        self.view = view
        self.authenticator = Authenticator()
        self.retrieve = Retriever(self.view.status)
        self._backup_directory = None
        self.mirror_model = None
        
    def save(self):
        if self.mirror_model is not None:
            loc = Storer().pickle_mirror(self.mirror_model, str(self.backup_directory), self.view.status)
        else:
            self.view.prompt("You haven't even loaded your settings yet!", False)
    
    def load(self):
        if self.backup_directory is None:
            self.view.prompt("You must select a backup directory before you load your data!", False)
            return
            
        if self.mirror_model is None:
            self.mirror_model = self.retrieve.pickled_mirror(str(self.backup_directory))
            self.view.status("Loaded flickrmirror settings.")
        
    def get_photo_count(self):
        if self.mirror_model:
            num = 0

            for s in self.mirror_model.filtered_sets():
                num += s.number_of_photos_and_videos

            return num
        else:
            return 0
            
    def authenticate(self):
        if not self.authenticator.authenticated():
            r = self.authenticator.start_authentication()
            self.view.prompt("Press enter once you're authenticated.", False)
            self.authenticator.end_authentication(r)
        
    def filter_set(self, sets):
        
        if sets == "*":
            self.mirror_model.filtered = []
            for set in self.mirror_model.sets:
                self.mirror_model.filtered.append(set.id)
                
            self.view.status("Filtered all sets.")
        else:
        
            sets = sets.split(',')
       
            for set_id_or_title in sets:
                set_id_or_title = set_id_or_title.strip()
                set = self.mirror_model.get_set(set_id_or_title)
                if set and set.id not in self.mirror_model.filtered:
                    self.mirror_model.filtered.append(set.id)
                    self.view.status("Filtered set %s" % set.title)
        
        self.save()
       
    def unfilter_set(self, sets):
        if sets == "*":
            self.mirror_model.filtered = []
        else:        
            sets = sets.split(',')

            for set_id_or_title in sets:
                set_id_or_title = set_id_or_title.strip()
                set = self.mirror_model.get_set(set_id_or_title)
                if set.id in self.mirror_model.filtered:
                    self.mirror_model.filtered.remove(set.id)
                    self.view.status("Un-filtered set %s" % set.title)
                    
        self.save()
        
    def mirror(self):
        self.load()
        self.view.status("Getting updated flickr information.")
        self.synchronize_server()
        self.view.status("Synchronizing with local hard drive.")
        self.synchronize_local()    
        self.save()
        
    def synchronize_server(self):
        """
        big, nasty method...  needs a lot of work...
        Don't worry about last updated...
        There is no way of knowing if sets get updated or anything
        so just get a new list of sets and compare
        """
        
        updated_mirror = MirrorModel(self.retrieve.sets(filled=True))
                
        sets_to_add = []
        for s in updated_mirror.sets:
            if self.mirror_model.has_set(s.id):
                current_set = self.mirror_model.get_set(s.id)
                current_set.update(s)
                for p in s.photos:

                    cp = current_set.get_photo(p.id)
                    if not cp:
                        current_set.append_photo(p)
                    elif cp.lastupdate < p.lastupdate:
                        current_set.remove_photo(cp)
                        cp.delete() #so it'll be updated... maybe a better way?
                        current_set.append_photo(p)
            else:
                sets_to_add.append(s)
                
        self.mirror_model.sets.extend(sets_to_add)
        
        sets_to_remove = []
        for current_set in self.mirror_model.sets:
            new_set = updated_mirror.get_set(current_set.id)
            if not new_set:
                sets_to_remove.append(current_set)
            else:                
                for photo in current_set.photos:
                    new_photo = new_set.get_photo(photo.id)
                    if not new_photo:
                        current_set.remove_photo(photo)
                        photo.delete()
        
        for set in sets_to_remove:
            self.mirror_model.sets.remove(set)
            set.delete()
        
        self.mirror_model.last_updated = int(time.time())
        self.save() # don't save until the end in case something goes wrong...
        
    def get_dirty_photos(self):
        dirty_photos = []
        
        for set in self.mirror_model.filtered_sets():
            for photo in set.photos:
                if photo.dirty:
                    dirty_photos.append( (photo, set) )
        
        return dirty_photos
        
    def retrieve_photo(self, photo, set, report=lambda x,y,z: x):
        set_storage = Directory(os.path.join(str(self.backup_directory), normalize(set.title)))
        self.download(photo, set_storage, report)
        self.save()
        
    def synchronize_local(self):
        dirty_photos = self.get_dirty_photos()
        
        for i in range(0, len(dirty_photos)):
            photo, set = dirty_photos[i]
            self.view.status("%i/%i - Downloading photo %s from set %s." % (
                i+1,
                len(dirty_photos),
                photo.title, 
                set.title
                )
            )
            self.retrieve_photo(photo, set)
            
        
    def download(self, photo, storage, report=lambda x,y,z: x):
        url = self.retrieve.url(photo)
        file_location = os.path.join(storage.path, photo.filename)
        
        if os.path.exists(file_location):
            self.view.status("%s already exists.  Skipping download." % photo.title)
        else:
            logger.info("downloading %s from %s" % (str(photo), url))
            #save to temp file first and then move to location so incomplete downloads are not saved
            tmp_file = urllib.urlretrieve(url, file_location, report)[0]
            shutil.move(tmp_file, file_location)
            self.view.status("Saved photo %s to %s." % (photo.title, file_location))
            logger.info("saved photo %s to %s" % (photo.title, file_location))
            
        photo.location = file_location
        photo.dirty = False
        
    def mirror_set(self, a_set):
        
        if type(a_set) == str:
            a_set = self.retrieve.set(a_set)
            self.retrieve.fill_set_with_photos(a_set)
            for photo in a_set.photos:
                self.view.status('retrieving photo info %s' % photo.title)
                self.retrieve.fill_photo_info(photo)
        
        storage = Directory(os.path.join(str(self.backup_directory), normalize(a_set.title)))
        
        for index in range(0, len(a_set.photos)):
            photo = a_set.photos[index]
            self.view.status("backing up photo %s(%s/%s)" % (
                photo.title,
                index + 1,
                len(a_set.photos)
            ), False)
            self.download(photo, storage)
            self.view.status("done...")
        
    def get_backup_directory(self):        
        return self._backup_directory

    def set_backup_directory(self, value):
        while not os.path.exists(value):
            value = raw_input("Enter your backup directory: ").strip()
                    
        self._backup_directory = Directory(value, False)
        
    backup_directory = property(get_backup_directory, set_backup_directory)
    
